package com.itheima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.article.dto.ArticleHomeDto;
import com.itheima.article.dto.ArticleInfoDto;
import com.itheima.article.mapper.ApArticleConfigMapper;
import com.itheima.article.mapper.ApArticleContentMapper;
import com.itheima.article.mapper.ApArticleMapper;
import com.itheima.article.pojo.ApArticle;
import com.itheima.article.pojo.ApArticleConfig;
import com.itheima.article.pojo.ApArticleContent;
import com.itheima.article.service.ApArticleService;
import com.itheima.article.vo.ArticleInfoVo;
import com.itheima.article.vo.HotArticleVo;
import com.itheima.common.constants.BusinessConstants;
import com.itheima.common.exception.LeadNewsException;
import com.itheima.common.vo.PageResultVo;
import com.itheima.common.vo.ResultVo;
import com.itheima.wemedia.feign.WmNewsFeign;
import com.itheima.wemedia.pojo.WmNews;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @version 1.0
 * @description <p>已发布的文章信息 业务实现</p>
 * @package com.itheima.article.service.impl
 */
@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    @Autowired
    private ApArticleContentMapper articleContentMapper;

    @Autowired
    private ApArticleConfigMapper articleConfigMapper;

    @Autowired
    private WmNewsFeign wmNewsFeign;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 同步自媒体文章到文章微服
     *
     * @param dto
     */
    @Override
    @Transactional
    public void saveArticleInfo(ArticleInfoDto dto) {
        log.info("文章微服务：开始同步文章信息");
        //添加文章表数据
        ApArticle apArticle = dto.getApArticle();
        //判断是否有旧文章id
        Long articleId = apArticle.getId();
        if (null != articleId) {
            //删除旧文章配置is_delete字段为已删除
            ApArticleConfig apArticleConfig = dto.getApArticleConfig();
            apArticleConfig.setIsDelete(1);
            articleConfigMapper.updateById(apArticleConfig);
            //删除文章内容和文章配置
            //建立单例map集合，批量删除数据
            /*Map<String, Object> singletonMap = Collections.singletonMap("article_id", articleId);
            articleContentMapper.deleteByMap(singletonMap);
            articleConfigMapper.deleteByMap(singletonMap);*/

        }
        //将id置为空值
        apArticle.setId(null);
        apArticle.setPublishTime(LocalDateTime.now());
        save(apArticle);
        log.info("添加文章表数据成功");
        //获取新增的文章id
        Long apArticleId = apArticle.getId();
        //添加文章内容
        ApArticleContent apArticleContent = dto.getApArticleContent();
        apArticleContent.setId(apArticleId);
        apArticleContent.setArticleId(apArticleId);
        articleContentMapper.insert(apArticleContent);
        log.info("添加文章内容成功");

        //保存文章配置
        ApArticleConfig apArticleConfig = dto.getApArticleConfig();
        apArticleConfig.setId(apArticleId);
        apArticleConfig.setArticleId(apArticleId);

        articleConfigMapper.insert(apArticleConfig);
        log.info("添加文章配置成功");
        //更新自媒体文章的状态
        WmNews wmNews = new WmNews();
        wmNews.setId(dto.getWmNewsId());

        wmNews.setArticleId(apArticleId);
        //状态设置为已发布
        wmNews.setStatus(BusinessConstants.WmNewsConstants.STATUS_PUBLISHED);
        //设置为上架
        wmNews.setEnable(BusinessConstants.WmNewsConstants.ENABLE_ON);
        ResultVo<WmNews> updateResult = wmNewsFeign.update(wmNews);

        if (!updateResult.isSuccess()) {
            throw new LeadNewsException("远程调用更新自媒体文章状态失败，文章数据同步");
        }
        log.info("远程调用更新自媒体文章状态成功");
    }

    /**
     * app端加载第一页数据
     *
     * @param dto
     * @param loadType
     * @return
     */
    @Override
    public PageResultVo<ApArticle> findPage(ArticleHomeDto dto, int loadType) {
        //参数检查
        dto.checkSize();
        //时间参数处理
        if (dto.getMaxBehotTime() == null)
            dto.setMaxBehotTime(new Date());
        if (dto.getMinBehotTime() == null)
            dto.setMinBehotTime(new Date());
        //设置分页参数
        IPage<ApArticle> page = new Page<>(dto.getLoaddir(), dto.getSize());
        //查询
        List<ApArticle> list = getBaseMapper().findPage(page, dto, loadType);
        //返回结果
        return PageResultVo.pageResult(dto.getLoaddir(), dto.getSize(), page.getTotal(), list);
    }

    /**
     * 查询文章详情
     *
     * @param paramMap
     * @return
     */
    @Override
    public ArticleInfoVo findArticleInfo(Map<String, String> paramMap) {
        //首先获取参数
        String articleId = paramMap.get("articleId");
        Long id = JSON.parseObject(articleId, Long.class);

        //通过id查询文章配置信息
        LambdaQueryWrapper<ApArticleConfig> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ApArticleConfig::getArticleId, id)
                .eq(ApArticleConfig::getIsDown, 0)
                .eq(ApArticleConfig::getIsDelete, 0);
        ApArticleConfig apArticleConfig = articleConfigMapper.selectOne(lqw);

        if(null == apArticleConfig){
            throw new LeadNewsException("文章已下架或已删除");
        }
        //通过id查询文章内容表
        ApArticleContent apArticleContent = articleContentMapper.selectById(id);

        //组装vo
        ArticleInfoVo vo = new ArticleInfoVo();
        vo.setApArticleConfig(apArticleConfig);
        vo.setApArticleContent(apArticleContent);
        return vo;
    }

    /**
     * 计算热点文章-定时任务
     */
    @Override
    public void computeAndSave2Redis() {
        //查询5天前的所有文章
        List<ApArticle> articleList = list5DaysBefore();
        //计算每篇文章的分数值，并转成vo
        List<HotArticleVo> voList = new ArrayList<>();
        for (ApArticle apArticle : articleList) {
            Integer score = computeScore(apArticle);
            HotArticleVo vo = HotArticleVo.build(apArticle,score);
            voList.add(vo);
        }
        //按每个频道分组
        Map<Integer, List<HotArticleVo>> channelMap = voList.stream().collect(Collectors.groupingBy(HotArticleVo::getChannelId));
        //遍历每个频道最多存30篇文章
        for (Integer channelId : channelMap.keySet()) {
            //取出每个频道的文章列表
            List<HotArticleVo> channelArticleList = channelMap.get(channelId);
            //按分数降序排序，并取前三十个
            List<HotArticleVo> max30Articles = channelArticleList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(30).collect(Collectors.toList());

            save2Redis(channelId.toString(),max30Articles);
        }
        //针对所有的频道进行热点文章排序，推荐频道默认为0
        //取分数最高的前三十个
        List<HotArticleVo> recommendList = voList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30).collect(Collectors.toList());
        save2Redis(BusinessConstants.ArticleConstants.DEFAULT_TAG,recommendList);
    }

    /**
     * 把文章存入redis的zset，key等于文章的频道，如果是推荐，则key为__all__
     * @param channelId
     * @param max30Articles
     */
    private void save2Redis(String channelId, List<HotArticleVo> max30Articles) {
        for (HotArticleVo article : max30Articles) {
            String key = BusinessConstants.ArticleConstants.HOT_ARTICLE_FIRST_PAGE + channelId;

            stringRedisTemplate.opsForZSet().add(key,article.getId().toString(),article.getScore());
        }
    }

    //计算文章分数
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        if(apArticle.getLikes() != null){
            score += apArticle.getLikes()*BusinessConstants.ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if(apArticle.getViews() != null){
            score += apArticle.getViews();
        }
        if(apArticle.getComment() != null){
            score += apArticle.getComment()*BusinessConstants.ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if(apArticle.getCollection() != null){
            score += apArticle.getCollection()*BusinessConstants.ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }

    /**
     * 获取5天前的文章列表
     * @return
     */
    private List<ApArticle> list5DaysBefore() {
        LocalDateTime fiveDaysBefore = LocalDateTime.now().minusDays(5);
        List<ApArticle> articleList = getBaseMapper().findBeforeDate(fiveDaysBefore);
        return articleList;
    }
}



